package editorarquivobmg.service;

import editorarquivobmg.codificacao.ICodificacao;
import editorarquivobmg.codificacao.Unicode;
import editorarquivobmg.gui.TelaPrincipal;
import editorarquivobmg.model.BmgFile;
import editorarquivobmg.model.DAT1;
import editorarquivobmg.model.FLI1;
import editorarquivobmg.model.FLW1;
import editorarquivobmg.model.INF1;
import editorarquivobmg.model.Ponteiro;
import editorarquivobmg.model.Texto;
import editorarquivobmg.util.ConversorTexto;
import editorarquivobmg.util.ManipuladorDados;
import java.io.File;
import java.util.ArrayList;

public class BmgFileService {

    private File arquivoBmgCorrente = null;
    private BmgFile bmgFilePrincipal = null;
    private BmgFile bmgFileComparacao = null;
    private ConversorTexto parser = null;
    private ICodificacao codificacao = new Unicode();

    public void instanciarArquivoBmgPrincipal(TelaPrincipal form, ManipuladorArquivoService manipuladorArquivo) {
        byte[] data;
        try {
            arquivoBmgCorrente = manipuladorArquivo.abrirArquivoBMG(form);
            data = ManipuladorDados.obterBytes(arquivoBmgCorrente);

            int numeroPonteiros = ManipuladorDados.obterValorDecimal(data, 40, 2);
            int tamanhoPonteiro = ManipuladorDados.obterValorDecimal(data, 42, 2);
            int sizeINF1 = ManipuladorDados.obterValorDecimal(data, 36, 4);
            byte[] dataINF1 = ManipuladorDados.obterBytesIntervalo(data, 32, 16);
            ArrayList<Ponteiro> ponteiros = iniciarPonteiros(data, sizeINF1, numeroPonteiros, tamanhoPonteiro);
            INF1 inf1 = new INF1(dataINF1, sizeINF1, numeroPonteiros, tamanhoPonteiro, ponteiros);

            ArrayList<Texto> textos = instanciarMessage(ManipuladorDados.obterBytesIntervalo(data, ManipuladorDados.obterEndereco(data, "DAT1"), ManipuladorDados.obterValorDecimal(data, ManipuladorDados.obterEndereco(data, "DAT1") + 4, 4)), inf1);
            byte[] cabecalho = ManipuladorDados.obterBytesIntervalo(data, ManipuladorDados.obterEndereco(data, "DAT1"), 10);
            int sizeDAT1 = ManipuladorDados.obterValorDecimal(data, ManipuladorDados.obterEndereco(data, "DAT1") + 4, 4);
            DAT1 dat1 = new DAT1(textos, cabecalho, sizeDAT1);

            int auxFLW1 = ManipuladorDados.obterEndereco(data, "FLW1"), sizeFLW1 = 0;
            byte[] dataFLW1 = null;
            boolean validoFLW1 = true;
            if (auxFLW1 == -1) {
                validoFLW1 = false;
            } else {
                dataFLW1 = ManipuladorDados.obterBytesIntervalo(data, auxFLW1, ManipuladorDados.obterValorDecimal(data, auxFLW1 + 4, 4));
                sizeFLW1 = dataFLW1.length;
            }
            FLW1 flw1 = new FLW1(dataFLW1, sizeFLW1, validoFLW1);

            int auxFLI1 = ManipuladorDados.obterEndereco(data, "FLI1"), sizeFLI1 = 0;
            byte[] dataFLI1 = null;
            boolean validoFLI1 = true;
            if (auxFLI1 == -1) {
                validoFLI1 = false;
            } else {
                dataFLI1 = ManipuladorDados.obterBytesIntervalo(data, auxFLI1);
                sizeFLI1 = dataFLI1.length;
            }
            FLI1 fli1 = new FLI1(dataFLI1, sizeFLI1, validoFLI1);

            bmgFilePrincipal = new BmgFile(inf1, dat1, flw1, fli1, data.length, ManipuladorDados.obterBytesIntervalo(data, 0, 32));
//            index = retornarPrimeiroIndiceValido(bmgFilePrincipal);
//            form.setTitle("Editor Arquivo BMG: " + arquivoBmgCorrente.getName());
//            form.getLblNum().setText((index + 1) + "");
//            if (parser != null) {
//                form.getTxtMessage().setText(parser.parseOutAllCodes(new String(bmgFilePrincipal.getDataTexto(index), codificacao.getCodificacao())));
//            } else {
//                form.getTxtMessage().setText(new String(bmgFilePrincipal.getDataTexto(index), codificacao.getCodificacao()));
//            }
            form.getTxtMessage().requestFocus();
            form.getTxtMessage().setCaretPosition(0);
            form.getLblQtdTexto().setText(bmgFilePrincipal.getNumeroPonteiros() + "");
            form.getBtnAnterior().setEnabled(true);
            form.getBtnProximo().setEnabled(true);
            form.getBtnSalvar().setEnabled(true);
            form.getBtnAbrir().setEnabled(true);
            form.getBtnLimpar().setEnabled(true);
            form.getTxtIr().setEnabled(true);
            form.getBtnIr().setEnabled(true);
            form.getBtnCarregarPixels().setEnabled(true);
            form.getBtnLimparPixels().setEnabled(true);
            form.getMenuExtrairScript().setEnabled(true);
            form.getMenuInserirScript().setEnabled(true);
            form.getMenuSalvar().setEnabled(true);
            form.getMenuSalvarComo().setEnabled(true);
            form.getBtnLimparMapeamento().setEnabled(true);
            form.getBtnCarregarMapeamento().setEnabled(true);
            form.getTxtMessage().setEnabled(true);
            form.getTxtMessageComparacao().setEnabled(true);
            form.getTxtPixels().setEnabled(true);
            form.getTxtMessageComparacao().setText("");
            form.getTblMapeamento().setEnabled(true);
            form.getRbMenuUnicode().setEnabled(true);
            form.getRbMenuUTF8().setEnabled(true);
            form.getRbMenuANSI().setEnabled(true);
            bmgFileComparacao = null;
        } catch (Exception ex) {
            System.err.println(ex.getMessage());
        }
    }

    public void instanciarArquivoBmgComparacao(TelaPrincipal form, ManipuladorArquivoService manipuladorArquivo, int index) {
        byte[] data;
        try {
            File arquivoBmgComparacao = manipuladorArquivo.abrirArquivoBMG(form);
            data = ManipuladorDados.obterBytes(arquivoBmgComparacao);

            int numeroPonteiros = ManipuladorDados.obterValorDecimal(data, 40, 2);
            int tamanhoPonteiro = ManipuladorDados.obterValorDecimal(data, 42, 2);
            int sizeINF1 = ManipuladorDados.obterValorDecimal(data, 36, 4);
            byte[] dataINF1 = ManipuladorDados.obterBytesIntervalo(data, 32, 16);
            ArrayList<Ponteiro> ponteiros = iniciarPonteiros(data, sizeINF1, numeroPonteiros, tamanhoPonteiro);
            INF1 inf1 = new INF1(dataINF1, sizeINF1, numeroPonteiros, tamanhoPonteiro, ponteiros);

            ArrayList<Texto> texto = instanciarMessage(ManipuladorDados.obterBytesIntervalo(data, ManipuladorDados.obterEndereco(data, "DAT1"), ManipuladorDados.obterValorDecimal(data, ManipuladorDados.obterEndereco(data, "DAT1") + 4, 4)), inf1);
            byte[] cabecalho = ManipuladorDados.obterBytesIntervalo(data, ManipuladorDados.obterEndereco(data, "DAT1"), 10);
            int sizeDAT1 = ManipuladorDados.obterValorDecimal(data, ManipuladorDados.obterEndereco(data, "DAT1") + 4, 4);
            DAT1 dat1 = new DAT1(texto, cabecalho, sizeDAT1);

            int aux = ManipuladorDados.obterEndereco(data, "FLW1"), sizeFLW1 = 0;
            byte[] dataFLW1 = null;
            boolean validoFLW1 = true;
            if (aux == -1) {
                validoFLW1 = false;
            } else {
                dataFLW1 = ManipuladorDados.obterBytesIntervalo(data, aux, ManipuladorDados.obterValorDecimal(data, aux + 4, 4));
                sizeFLW1 = dataFLW1.length;
            }
            FLW1 flw1 = new FLW1(dataFLW1, sizeFLW1, validoFLW1);

            int auxFLI1 = ManipuladorDados.obterEndereco(data, "FLI1"), sizeFLI1 = 0;
            byte[] dataFLI1 = null;
            boolean validoFLI1 = true;
            if (auxFLI1 == -1) {
                validoFLI1 = false;
            } else {
                dataFLI1 = ManipuladorDados.obterBytesIntervalo(data, auxFLI1);
                sizeFLI1 = dataFLI1.length;
            }
            FLI1 fli1 = new FLI1(dataFLI1, sizeFLI1, validoFLI1);

            bmgFileComparacao = new BmgFile(inf1, dat1, flw1, fli1, data.length, ManipuladorDados.obterBytesIntervalo(data, 0, 32));
//
//            if (parser != null) {
//                form.getTxtMessageComparacao().setText(parser.parseOutAllCodes(new String(bmgFileComparacao.getDataTexto(index), codificacao.getCodificacao())));
//            } else {
//                form.getTxtMessageComparacao().setText(new String(bmgFileComparacao.getDataTexto(index), codificacao.getCodificacao()));
//            }
        } catch (Exception ex) {
            System.err.println(ex.getMessage());
        }
    }

    public void salvar(TelaPrincipal form, File arquivoBmgCorrente, int index) {
        try {
            if (bmgFilePrincipal != null) {
                if (parser != null) {
                    modificarTexto(form.getTxtMessage().getText(), index, parser);
                    form.getTxtMessage().setText(parser.parseOutAllCodes(new String(bmgFilePrincipal.getDataTexto(index), codificacao.getCodificacao())));
                } else {
                    modificarTexto(form.getTxtMessage().getText(), index);
                    form.getTxtMessage().setText(new String(bmgFilePrincipal.getDataTexto(index), codificacao.getCodificacao()));
                }
                form.setTitle("Editor Arquivo BMG: *" + arquivoBmgCorrente.getName());
            }
        } catch (Exception ex) {
            System.err.println(ex.getMessage());
        }
    }

    public void modificarEndereco(int diferenca, int pos) {

        int enderecoTexto = bmgFilePrincipal.getEnderecoTextoPonteiro(pos);
        enderecoTexto += diferenca;

        bmgFilePrincipal.modificarEndereco(enderecoTexto, pos);

        byte[] data = bmgFilePrincipal.getDataPonteiro(pos);
        for (int i = 0; i < 4; i++) {
            data[i] = 0;
        }

        String aux = ManipuladorDados.intToHexString(enderecoTexto);

        byte[] q = ManipuladorDados.hexStringToByteArray(aux);
        q = ManipuladorDados.inverterBytes(q);
        System.arraycopy(q, 0, data, 0, q.length);

        bmgFilePrincipal.setDataPonteiro(data, pos);
    }

    private ArrayList<Ponteiro> iniciarPonteiros(byte[] data, int size, int numeroPonteiros, int tamanhoPonteiro) {
        ArrayList<Ponteiro> pointer = new ArrayList<Ponteiro>();
        Ponteiro p;
        byte[] b;
        int k = 48, addr;
        for (int i = 0; i < numeroPonteiros - 1; i++) {
            b = new byte[tamanhoPonteiro];
            for (int j = 0; j < tamanhoPonteiro; j++) {
                b[j] = data[k];
                k++;
            }
            int enderecoTexto = ManipuladorDados.obterValorDecimal(b, 0, 4);
            boolean valido = true;
            if (enderecoTexto == 0) {
                valido = false;
            }
            p = new Ponteiro(b, enderecoTexto, valido);
            pointer.add(p);
        }
        addr = (numeroPonteiros - 1) * tamanhoPonteiro + 48 + tamanhoPonteiro;
        if (size + 32 > addr) {
            b = new byte[size + 32 - addr + tamanhoPonteiro];
            for (int i = 0; i < size + 32 - addr + tamanhoPonteiro; i++) {
                b[i] = data[k];
                k++;
            }
            int enderecoTexto = ManipuladorDados.obterValorDecimal(b, 0, 4);
            boolean valido = true;
            if (enderecoTexto == 0) {
                valido = false;
            }
            p = new Ponteiro(b, enderecoTexto, valido);
            pointer.add(p);
        } else {
            b = new byte[tamanhoPonteiro];
            for (int j = 0; j < tamanhoPonteiro; j++) {
                b[j] = data[k];
                k++;
            }
            int enderecoTexto = ManipuladorDados.obterValorDecimal(b, 0, 4);
            boolean valido = true;
            if (enderecoTexto == 0) {
                valido = false;
            }
            p = new Ponteiro(b, enderecoTexto, valido);
            pointer.add(p);
        }
        return pointer;
    }

    public void modificarTexto(String texto, int numeroTexto, ConversorTexto parser) throws Exception {
        texto = parser.parseInAllCodes(texto);

        int tamanhoMessageNovo = texto.getBytes(codificacao.getCodificacao()).length;
        int tamanhoMessageAntigo = bmgFilePrincipal.getSizeTexto(numeroTexto);
        int diferenca = tamanhoMessageNovo - tamanhoMessageAntigo;


        for (int j = numeroTexto + 1; j < bmgFilePrincipal.getNumeroPonteiros(); j++) {
            if (bmgFilePrincipal.ponteiroIsValido(j)) {
                modificarEndereco(diferenca, j);
            }
        }
        bmgFilePrincipal.modificarTexto(texto.getBytes(codificacao.getCodificacao()), numeroTexto);
    }

    public void modificarTexto(String texto, int numeroTexto) throws Exception {
        int tamanhoMessageNovo = texto.getBytes(codificacao.getCodificacao()).length;
        int tamanhoMessageAntigo = bmgFilePrincipal.getSizeTexto(numeroTexto);
        int diferenca = tamanhoMessageNovo - tamanhoMessageAntigo;

        for (int j = numeroTexto + 1; j < bmgFilePrincipal.getNumeroPonteiros(); j++) {
            if (bmgFilePrincipal.ponteiroIsValido(j)) {
                modificarEndereco(diferenca, j);
            }
        }
        bmgFilePrincipal.modificarTexto(texto.getBytes(codificacao.getCodificacao()), numeroTexto);
    }

    public void limparBmgFileComparacao(TelaPrincipal form) {
        form.getTxtMessageComparacao().setText("");
        bmgFileComparacao = null;
    }

    private ArrayList<Texto> instanciarMessage(byte[] data, INF1 inf1) {
        int msgSize, k;
        byte[] b;
        int i = 0, j = 0;
        boolean flag = true;
        ArrayList<Texto> textos = new ArrayList<Texto>();
        while (i < inf1.getNumeroPonteiros()) {
            if (inf1.ponteiroIsValido(i)) { //verifica se o ponteiro atual é valido
                k = inf1.getEnderecoTextoPonteiro(i) + 8;
                if (i < inf1.getNumeroPonteiros() - 1) { //verifica se o ponteiro não é o último ponteiro da tabela
                    j = i + 1;
                    while (!inf1.ponteiroIsValido(j)) {
                        if (j < inf1.getNumeroPonteiros() - 1) { //verifica se o ponteiro proximo não é o ultimo
                            j++;
                        } else { //o ponteiro próximo é o último
                            msgSize = data.length - inf1.getEnderecoTextoPonteiro(i) - 8;
                            b = new byte[msgSize];
                            for (int h = 0; h < msgSize; h++) {
                                b[h] = data[k];
                                k++;
                            }
                            Texto tx = new Texto(b, b.length);
                            textos.add(tx);
                            flag = false;
                            break;
                        }
                    }
                    if (flag) { //foi encontrado um ponteiro proximo válido
                        msgSize = inf1.getEnderecoTextoPonteiro(j) - inf1.getEnderecoTextoPonteiro(i);
                        b = new byte[msgSize];
                        for (int h = 0; h < msgSize; h++) {
                            b[h] = data[k];
                            k++;
                        }
                        textos.add(new Texto(b, b.length));
                        flag = true;
                        i++;
                    } else {
                        break;
                    }
                } else { // o ponteiro é o último da lista de ponteiros
                    msgSize = data.length - inf1.getEnderecoTextoPonteiro(inf1.getNumeroPonteiros() - 1) - 8;
                    b = new byte[msgSize];
                    for (int l = 0; l < msgSize; l++) {
                        b[l] = data[k];
                        k++;
                    }
                    textos.add(new Texto(b, b.length));
                    break;
                }
            } else {//o poteiro atual não é valido
                textos.add(new Texto(new byte[0], 0));
                i++;
            }
        }
        return textos;
    }

//    private int retornarPrimeiroIndiceValido(BmgFile bmgFilePrincipal) {
//        int i = 0;
//        while (!bmgFilePrincipal.ponteiroIsValido(i)) {
//            i++;
//        }
//        return i;
//    }
    public BmgFile getBmgFilePrincipal() {
        return bmgFilePrincipal;
    }

    public BmgFile getBmgFileComparacao() {
        return bmgFileComparacao;
    }

    public ConversorTexto getParser() {
        return parser;
    }

    public ICodificacao getCodificacao() {
        return codificacao;
    }

    public File getArquivoBmgCorrente() {
        return arquivoBmgCorrente;
    }
}